use anyhow::anyhow;
use std::{fmt::Display, sync::mpsc, thread};

const NUM_PRODUCES: usize = 4;

#[derive(Debug)]
struct CusMessage {
    index: usize,
    value: usize,
}

fn main() -> anyhow::Result<()> {
    let (tx, rx) = mpsc::channel();
    for i in 0..NUM_PRODUCES {
        let tx = tx.clone();
        thread::spawn(move || producer(i, tx));
    }
    drop(tx);

    let consumer = thread::spawn(move || {
        for msg in rx {
            println!("{}", msg);
        }
        rand::random::<u8>()
    });

    let exit_code = consumer
        .join()
        .map_err(|e| anyhow!("Thread join error: {:?}", e))?;
    println!("consumer exit code is {}", exit_code);

    Ok(())
}

fn producer(index: usize, tx: mpsc::Sender<CusMessage>) -> anyhow::Result<()> {
    loop {
        let value = rand::random::<usize>();
        tx.send(CusMessage::new(index, value))?;
        let sleep_time = rand::random::<u8>() as u64 * 10;
        thread::sleep(std::time::Duration::from_millis(sleep_time));
        if rand::random::<u8>() % 5 == 0 {
            break;
        }
    }
    println!("Notice: producer {} exit now", index);
    Ok(())
}

impl CusMessage {
    fn new(index: usize, value: usize) -> Self {
        Self { index, value }
    }
}

impl Display for CusMessage {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "CusMessage: (thread_id: {}, Value: {})",
            self.index, self.value
        )?;
        Ok(())
    }
}
